﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;

namespace EBA.Dynamic
{

    /// <summary>
    /// see http://msdn.microsoft.com/zh-cn/library/812xyxy2.aspx
    /// </summary>
    public static class DynamicEmit
    {

        public static ILGenerator IfEqualsNull(this ILGenerator il, FieldInfo fieldInfo, Label gotoFalse)
        {
            //bool result;
            var result = il.DeclareLocal(typeof(bool));
           
            //result = this.Field == null
            il.Emit(OpCodes.Nop);
            il.Emit(OpCodes.Ldfld, fieldInfo);
            il.Emit(OpCodes.Ldnull);
            il.Emit(OpCodes.Ceq);
            il.Emit(OpCodes.Stloc_S, result);

            //if(result==false){gotoFalse}
            il.Emit(OpCodes.Ldloc_S, result);//result
            il.Emit(OpCodes.Ldc_I4_0);//false
            il.Emit(OpCodes.Ceq);
            il.Emit(OpCodes.Brtrue_S,gotoFalse);

            return il;

            //if(field == null){ trueLabel;}else { falseLable}
            //return il.Ldfld(fieldInfo).Ldnull();
        }

        public static ILGenerator Call(this ILGenerator il, MethodInfo methodInfo, params Action<ILGenerator>[] arguments)
        {
            il.Emit(OpCodes.Nop);
            if (arguments != null)
            {
                foreach (var args in arguments)
                {
                    args(il);
                }
            }

            il.Emit(OpCodes.Call, methodInfo);
            return il;

        }

        // public static ILGenerator EqualsNull(this ILGenerator 

        /// <summary>
        /// Compares two values. If they are equal, the integer value 1 (int32) is pushed onto the evaluation stack; otherwise 0 (int32) is pushed onto the evaluation stack.
        /// </summary>
        /// <param name="il"></param>
        /// <returns></returns>
        //public static ILGenerator Ceq(this ILGenerator il)
        //{
        //}

        public static ILGenerator Nop(this ILGenerator il)
        {
            il.Emit(OpCodes.Nop);
            return il;
        }

        public static ILGenerator Ret(this ILGenerator il)
        {
            il.Emit(OpCodes.Ret);
            return il;
        }

        /// <summary>
        /// 推送对元数据中存储的字符串的新对象引用。
        /// </summary>
        /// <param name="il"></param>
        /// <param name="source"></param>
        /// <returns></returns>
        public static ILGenerator Ldstr(this ILGenerator il, string source)
        {
            il.Emit(OpCodes.Ldstr, source);
            return il;
        }

        /// <summary>
        /// 将参数（由指定索引值引用）加载到堆栈上。
        /// </summary>
        /// <param name="il"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        public static ILGenerator Ldarg(this ILGenerator il, int index)
        {
            switch (index)
            {
                case 0:
                    il.Emit(OpCodes.Ldarg_0);
                    break;
                case 1:
                    il.Emit(OpCodes.Ldarg_1);
                    break;
                case 2:
                    il.Emit(OpCodes.Ldarg_2);
                    break;
                case 3:
                    il.Emit(OpCodes.Ldarg_3);
                    break;
                default:
                    il.Emit(OpCodes.Ldarg_S, (sbyte)index);
                    break;
            }
            return il;
        }

        public static ILGenerator Ldfld(this ILGenerator il, FieldInfo field)
        {
            il.Emit(OpCodes.Ldfld, field);
            return il;
        }

        public static ILGenerator Pop(this ILGenerator il)
        {
            il.Emit(OpCodes.Pop);
            return il;
        }

        public static ILGenerator Callvirt(this ILGenerator il, MethodInfo method)
        {
            il.Emit(OpCodes.Callvirt, method);
            return il;
        }

        public static ILGenerator Ldnull(this ILGenerator il)
        {
            il.Emit(OpCodes.Ldnull);
            return il;
        }

        /// <summary>
        /// 将所提供的 int32 类型的值作为 int32 推送到计算堆栈上。
        /// </summary>
        public static ILGenerator Ldc_I4(this ILGenerator il, int value)
        {
            switch (value)
            {
                case 0:
                    il.Emit(OpCodes.Ldc_I4_0);
                    break;
                case 1:
                    il.Emit(OpCodes.Ldc_I4_1);
                    break;
                case 2:
                    il.Emit(OpCodes.Ldc_I4_2);
                    break;
                case 3:
                    il.Emit(OpCodes.Ldc_I4_3);
                    break;
                case 4:
                    il.Emit(OpCodes.Ldc_I4_4);
                    break;
                case 5:
                    il.Emit(OpCodes.Ldc_I4_5);
                    break;
                case 6:
                    il.Emit(OpCodes.Ldc_I4_6);
                    break;
                case 7:
                    il.Emit(OpCodes.Ldc_I4_7);
                    break;
                case 8:
                    il.Emit(OpCodes.Ldc_I4_8);
                    break;
                default:
                    il.Emit(OpCodes.Ldc_I4, value);
                    break;
            }

            return il;
        }

        /// <summary>
        /// 用计算堆栈上的对象 ref 值（O 类型）替换给定索引处的数组元素。
        /// </summary>
        /// <param name="il"></param>
        /// <returns></returns>
        public static ILGenerator Stelem_Ref(this ILGenerator il)
        {
            il.Emit(OpCodes.Stelem_Ref);
            return il;
        }

        /// <summary>
        /// 无条件地将控制转移到目标指令（短格式）。
        /// </summary>
        /// <param name="il"></param>
        /// <param name="label"></param>
        /// <returns></returns>
        public static ILGenerator Br_s(this ILGenerator il, Label label)
        {
            il.Emit(OpCodes.Br_S, label);
            return il;
        }

        /// <summary>
        /// 将对新的从零开始的一维数组（其元素属于特定类型）的对象引用推送到计算堆栈上。
        /// </summary>
        /// <param name="il"></param>
        /// <param name="elementType"></param>
        /// <param name="len"></param>
        /// <returns></returns>
        public static ILGenerator Newarr(this ILGenerator il, Type elementType, int len)
        {
            il.Ldc_I4(len);
            il.Emit(OpCodes.Newarr, elementType);

            return il;

        }

        /// <summary>
        /// 从计算堆栈的顶部弹出当前值并将其存储到指定索引处的局部变量列表中。
        /// </summary>
        /// <param name="il"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        public static ILGenerator Stloc(this ILGenerator il, int index)
        {
            switch (index)
            {
                case 0:
                    il.Emit(OpCodes.Stloc_0);
                    break;
                case 1:
                    il.Emit(OpCodes.Stloc_1);
                    break;
                case 2:
                    il.Emit(OpCodes.Stloc_2);
                    break;
                case 3:
                    il.Emit(OpCodes.Stloc_3);
                    break;
                default:
                    il.Emit(OpCodes.Stloc_S, (sbyte)index);
                    break;
            }
            return il;
        }
        public static ILGenerator Ldloc(this ILGenerator il, LocalBuilder localBuilder)
        {
            il.Emit(OpCodes.Ldloc_S, localBuilder);
            return il;
        }

        public static ILGenerator Castclass(this ILGenerator il, Type type)
        {
            il.Emit(OpCodes.Castclass, type);
            return il;
        }

        /// <summary>
        /// 将指定索引处的局部变量加载到计算堆栈上。
        /// </summary>
        /// <param name="il"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        public static ILGenerator Ldloc(this ILGenerator il, int index)
        {
            switch (index)
            {
                case 0:
                    il.Emit(OpCodes.Ldloc_0);
                    break;
                case 1:
                    il.Emit(OpCodes.Ldloc_1);
                    break;
                case 2:
                    il.Emit(OpCodes.Ldloc_2);
                    break;
                case 3:
                    il.Emit(OpCodes.Ldloc_3);
                    break;
                default:
                    il.Emit(OpCodes.Ldloc_S, (sbyte)index);
                    break;
            }
            return il;
        }

    }
}
